import Designer from '@/index';
import EventEmitter from './eventEmitter';
import Canvas from './canvas';
import Stage from './stage';
import {
  getMinEnclosingRect,
  getRealRect,
  getType,
  transformedCoord,
  uuid,
  draw,
  normalizeRotation,
  isDef,
  isPointInsideCircle,
  vectorRotation,
} from '@/utils';

/**
 * 节点类
 */
export default class Node extends EventEmitter {
  className = 'Node'; // 类名
  parent = null; // 父级实例
  constructor(config = {}) {
    super();
    this.customData = config.customData ?? null; // 自定义节点数据
    this.x = config.x ?? 0; // 相对父级节点的 x 坐标
    this.y = config.y ?? 0; // 相对父级节点的 y 坐标
    this.width = config.width ?? 0; // 宽
    this.height = config.height ?? 0; // 高
    this.id = config.id ?? uuid(); // id
    this.name = config.name ?? ''; // name
    this.opacity = config.opacity ?? 1; // 不透明度
    this.shadowOffsetX = config.shadowOffsetX ?? 0; // 阴影水平偏移距离
    this.shadowOffsetY = config.shadowOffsetY ?? 0; // 阴影垂直偏移距离
    this.shadowBlur = config.shadowBlur ?? 0; // 模糊效果程度
    this.shadowColor = config.shadowColor ?? 'transparent'; // 阴影颜色
    this.rotation = config.rotation ?? 0; // 旋转角度
    this.scale = config.scale ?? 1; // 缩放值
    this.flipX = config.flipX ?? false; // 是否水平翻转
    this.flipY = config.flipY ?? false; // 是否垂直翻转
    this.zIndex = config.zIndex ?? 0; // 深度属性值
    this.visible = config.visible ?? true; // 是否可见
    this.draggable = config.draggable ?? false; // 是否可拖动
    // 设置锚点集
    this.setAnchors(config.anchors, true);
    // 配置项父级节点为对象，设置父级节点并添加当前节点
    if (getType(config.parent) === 'Object') {
      config.parent.nodes.push(this);
      this.parent = config.parent;
    }
  }
  // 锁定状态
  get locked() {
    return !this.draggable;
  }
  // 是否带锚点
  get hasAnchors() {
    return Array.isArray(this.anchors);
  }
  // 空锚点状态
  get emptyAnchors() {
    return this.hasAnchors && !this.anchors.length;
  }
  // 获取父级数组
  get parentArr() {
    const arr = [];
    let { parent } = this;
    if (!parent) return arr;
    arr.push(parent);
    while (parent.className !== 'Stage') {
      parent = parent.parent;
      if (!parent) return arr;
      arr.push(parent);
    }
    return arr;
  }
  // 父级是否是stage实例
  get isStageParent() {
    return this.parent?.className === 'Stage';
  }
  // 获取stage实例
  get stage() {
    const lastParent = this.parentArr.pop();
    return lastParent?.className === 'Stage' ? lastParent : null;
  }
  // 获取最顶层父级编组
  get topGroup() {
    return this.parentArr.filter((i) => i.className === 'Group').pop();
  }
  // 获取最顶层画板节点
  get topBoard() {
    return this.parentArr.filter((i) => i.className === 'Board').pop();
  }
  // 获取实际绘制区域矩形
  get realRect() {
    const { x, y, width, height, rotation, scale } = this;
    return getRealRect(x, y, width, height, rotation, scale);
  }
  // 获取相对网格坐标的矩形
  get gridRect() {
    const { isStageParent, realRect } = this;
    if (isStageParent) return realRect;
    const { points, center } = realRect;
    const gridPoints = points.map((point) => this.getGridCoordByParentCoord(point));
    const gridCenter = this.getGridCoordByParentCoord(center);
    return {
      points: gridPoints,
      edges: gridPoints.map((point, index, array) => ({ o: point, d: array[(index + 1) % array.length] })),
      center: gridCenter,
    };
  }
  // 获取实际绘制区域最小外接矩形
  get minEnclosingRect() {
    const { points } = this.realRect;
    return getMinEnclosingRect(points);
  }
  // 获取相对网格坐标的最小外接矩形
  get gridMinEnclosingRect() {
    const { points } = this.gridRect;
    return getMinEnclosingRect(points);
  }
  // 获取兄弟节点
  get siblings() {
    return this.parent.nodes.filter((node) => node !== this);
  }
  // 相对网格坐标的旋转角度
  get gridRotation() {
    const { isStageParent, parent, rotation } = this;
    if (isStageParent) return rotation;
    return parent.gridRotation + rotation;
  }
  // 相对网格坐标的缩放值
  get gridScale() {
    const { isStageParent, parent, scale } = this;
    if (isStageParent) return scale;
    return parent.gridScale * scale;
  }
  // 获取当前鼠标位置在节点中的相对坐标
  get offsetCoord() {
    const { parent, stage } = this;
    if (!parent || !stage) return;
    return this.getOffsetCoord(stage.gridCoord);
  }
  // 默认锚点集（默认四个边中心点）
  get defaultAnchors() {
    const { width, height } = this;
    if (!width || !height) return [];
    const radius = 4;
    return [
      { id: 'top', name: 'top', x: width / 2, y: 0, radius },
      { id: 'right', name: 'right', x: width, y: height / 2, radius },
      { id: 'bottom', name: 'bottom', x: width / 2, y: height, radius },
      { id: 'left', name: 'left', x: 0, y: height / 2, radius },
    ];
  }
  // 私有锚点集
  get _anchors() {
    if (!this.hasAnchors) return [];
    const { anchors, gridScale, gridRect } = this;
    const { gridCoord, hoveringAnchor } = this.stage;
    return anchors.reduce((arr, anchor, index) => {
      const { visible, id, x, y, radius } = anchor;
      const _anchor = {
        ...anchor,
        visible: visible !== false && isDef(id),
        node: this,
        index,
        hovering: hoveringAnchor?.node === this && hoveringAnchor?.id === id,
      };
      arr.push(_anchor);
      if (!_anchor.visible) return arr;
      // 网格坐标
      const gridC = this.getGridCoord({ x, y });
      // 连线点
      _anchor.p = gridC;
      // hover 判定
      _anchor.isPointIn = isPointInsideCircle(gridCoord, gridC, 2 * radius * gridScale);
      // 锚点射出角度（用于平滑节点连线的锚点出入）
      _anchor.v = vectorRotation(gridRect.center, gridC);
      return arr;
    }, []);
  }
  // 获取锚点集的映射对象
  get anchorsMap() {
    return this._anchors.reduce((map, anchor) => ((map[anchor.id] = anchor), map), {});
  }
  // 更新节点
  update() {}
  // 更新父级节点
  updateParent() {
    this.parentArr.forEach((parent) => parent.update?.());
  }
  // 缩放值计算
  calc(num, stage = this.stage) {
    return (num / stage.scale) * stage.pixelRatio;
  }
  // 判断点是否在节点图形中
  isPointIn() {
    const { width, height, offsetCoord } = this;
    const ctx = Stage.canvas.getContext();
    const path = new Path2D();
    path.rect(0, 0, width, height);
    return ctx.isPointInPath(path, offsetCoord.x, offsetCoord.y);
  }
  // 根据画布网格坐标获取节点相对坐标
  getOffsetCoord(gridCoord) {
    const { isStageParent, parent, x, y, width, height, rotation, scale } = this;
    const parentCoord = isStageParent ? gridCoord : parent.getOffsetCoord(gridCoord);
    const offset = { x: parentCoord.x - x, y: parentCoord.y - y };
    const center = { x: width / 2, y: height / 2 };
    // 将相对坐标 以 节点中心点为原点 反向旋转旋转角度 反比例缩放缩放值 得到旋转及缩放前的节点相对坐标
    return transformedCoord(offset, center, -rotation, 1 / scale);
  }
  // 根据节点相对坐标获取画布网格坐标
  getGridCoord(offsetCoord) {
    const { isStageParent, parent, x, y, width, height, rotation, scale } = this;
    const center = { x: width / 2, y: height / 2 };
    // 将节点相对坐标 以 节点中心点为原点 旋转旋转角度 缩放缩放值 得到旋转及缩放后的相对父级节点坐标
    const rotatedCoord = transformedCoord(offsetCoord, center, rotation, scale);
    const parentCoord = { x: rotatedCoord.x + x, y: rotatedCoord.y + y };
    if (isStageParent) return parentCoord;
    return parent.getGridCoord(parentCoord);
  }
  // 根据画布网格坐标获取相对父级节点坐标
  getParentCoordByGridCoord(gridCoord) {
    const { isStageParent, parent } = this;
    if (isStageParent) return gridCoord;
    return parent.getOffsetCoord(gridCoord);
  }
  // 根据相对父级节点坐标获取画布网格坐标
  getGridCoordByParentCoord(parentCoord) {
    const { isStageParent, parent } = this;
    if (isStageParent) return parentCoord;
    return parent.getGridCoord(parentCoord);
  }
  // 根据节点相对坐标获取画布全局坐标
  getGlobalCoord(offsetCoord) {
    const { stage, gridRotation, gridScale } = this;
    if (!stage) return;
    // 获取画布网格坐标
    const grid = this.getGridCoord(offsetCoord);
    // 获取画布相对坐标
    const offset = stage.getOffsetCoord(grid);
    // 获取画布绝对坐标
    const absolute = stage.getAbsoluteCoord(offset);
    // 获取相对于浏览器窗口的坐标
    const fixed = stage.getFixedCoord(absolute);
    return {
      grid,
      offset,
      absolute,
      fixed,
      rotate: gridRotation,
      scale: (gridScale * stage.scale) / stage.pixelRatio,
    };
  }
  // 绘制主要内容
  drawMain(ctx, isExport = false) {}
  // 绘制锚点
  drawAnchors(ctx) {
    const { _anchors } = this;
    _anchors.forEach(({ visible, x, y, radius, hovering }) => {
      if (!visible) return;
      draw.arc(ctx, { x, y, radius, color: 'white' });
      draw.arc(ctx, { x, y, radius, isFill: false });
      if (hovering) draw.arc(ctx, { x, y, radius });
    });
  }
  /**
   * Methods
   */
  /**
   * 绘制
   */
  draw(ctx) {
    if (!ctx) return;
    if (!this.visible) return;
    if (!this.parent) return;
    const {
      x,
      y,
      width,
      height,
      rotation,
      scale,
      flipX,
      flipY,
      opacity,
      shadowOffsetX,
      shadowOffsetY,
      shadowBlur,
      shadowColor,
      stage,
    } = this;
    // 保存当前状态
    ctx.save();
    // 将canvas坐标平移到节点位置
    ctx.translate(x, y);
    // 使用中心点旋转及缩放，旋转和缩放的顺序很重要，先旋转后缩放，否则会导致图像倾斜
    ctx.translate(width / 2, height / 2);
    // 旋转
    ctx.rotate(rotation * (Math.PI / 180));
    // 缩放
    ctx.scale(scale, scale);
    // 翻转
    ctx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
    // 恢复到缩放及旋转前的坐标
    ctx.translate(-width / 2, -height / 2);
    // 设置图形和图片透明度
    ctx.globalAlpha *= opacity;
    // 阴影
    ctx.shadowOffsetX = shadowOffsetX * stage.scale; // x 轴偏移量
    ctx.shadowOffsetY = shadowOffsetY * stage.scale; // y 轴偏移量
    ctx.shadowBlur = shadowBlur * stage.scale; // 阴影模糊度
    ctx.shadowColor = shadowColor; // 阴影颜色
    // 绘制主要内容
    this.drawMain(ctx);
    // 绘制锚点
    this.drawAnchors(ctx);
    // 恢复被保存的状态
    ctx.restore();
  }
  /**
   * 绘制轮廓
   */
  drawOutline(ctx, config) {
    const { parent, stage } = this;
    if (!parent || !stage) return;
    const calc = (num) => this.calc(num, stage);
    const { edges, points } = this.gridRect;
    const selected = stage.selectedNodes.some((node) => node === this); // 节点已选中
    draw.outline(ctx, edges, {
      lineWidth: calc(Designer.outlineLineWidth), // 线宽
      lineDash: selected ? [] : [calc(3), calc(2)], // 节点已选中：实线 否则 虚线
      ...config,
    });
    // 绘制🔒
    if (this.locked && !config?.guideOutline) {
      draw.arc(ctx, { x: points[2].x, y: points[2].y, radius: calc(6) });
      draw.text(ctx, { text: '🔒', x: points[2].x, y: points[2].y, fontSize: calc(8), align: 'center' });
    }
  }
  /**
   * 获取缩放值
   */
  getScale() {
    return this.scale;
  }
  /**
   * 设置缩放值
   * @param {number} scale
   */
  setScale(scale) {
    this.scale = scale;
    this.updateParent();
    this.stage?.transformer.update(this);
    // 重绘
    this.stage?.draw();
  }
  /**
   * 设置/获取旋转角度
   * @param {number} rotation
   */
  rotate(rotation) {
    if (getType(rotation) !== 'Number') return this.rotation;
    this.rotation = normalizeRotation(rotation);
    this.updateParent();
    this.stage?.transformer.update(this);
    // 重绘
    this.stage?.draw();
  }
  /**
   * 从父级中移除当前节点
   */
  remove(onlyRemove = false) {
    const { parent, parentArr, stage, id } = this;
    if (!parent) return;
    this.parent = null;
    const index = parent.nodes.findIndex((node) => node === this);
    if (index >= 0) parent.nodes.splice(index, 1);
    // 删除关联的节点连线
    stage.removeEdge({ source: id }, true);
    stage.removeEdge({ target: id }, true);
    if (onlyRemove || !stage) return;
    // 更新父级节点
    parentArr.forEach((parent) => parent.update?.());
    const { transformer, selectedNodes } = stage;
    // 转换器中有当前节点，清空已选节点
    if (transformer.nodes.some((i) => i === this)) stage.setSelectedNodes([]);
    // 转换器中只有当前节点的父级节点 且 该父级节点未被移除，设置已选节点为该父级节点
    else if (transformer.nodes.length === 1 && parentArr.some((i) => i === transformer.nodes[0] && i.parent)) {
      stage.setSelectedNodes(transformer.nodes);
    }
    // 已选节点中有当前节点，清空已选节点
    else if (selectedNodes.some((i) => i === this)) stage.setSelectedNodes([]);
    // 其他情况，只重绘
    else stage.draw();
    stage.handleRecord();
  }
  /**
   * 节点克隆
   * @param {*} overrideConfig
   */
  clone(overrideConfig) {
    const newNode = Designer.clone(this, overrideConfig);
    this.parent.add(newNode);
    return newNode;
  }
  /**
   * 设置层级关系
   * @param {'top' | 'bottom' | 'up' | 'down' | number} zIndex
   */
  setZIndex(zIndex) {
    // 数字赋值
    if (getType(zIndex) === 'Number') this.zIndex = zIndex;
    const { siblings } = this;
    // 按照深度属性排序兄弟节点
    siblings.sort((a, b) => a.zIndex - b.zIndex);
    // 置顶
    if (zIndex === 'top') {
      // 获取层级最大节点
      const top = siblings.pop();
      // 当前层级值 = 层级最大节点层级值 + 1
      if (top && this.zIndex <= top.zIndex) this.zIndex = top.zIndex + 1;
    }
    // 置底
    else if (zIndex === 'bottom') {
      // 获取层级最小节点
      const bottom = siblings.shift();
      // 当前层级值 = 层级最小节点层级值 - 1
      if (bottom && this.zIndex >= bottom.zIndex) this.zIndex = bottom.zIndex - 1;
    }
    // 上移
    else if (zIndex === 'up') {
      // 寻找层级最小节点下标
      let index = siblings.findIndex((node) => this.zIndex <= node.zIndex);
      if (index !== -1) {
        // 当前层级值 = 层级最小节点层级值 + 1
        this.zIndex = siblings[index].zIndex + 1;
        // 之后的节点 层级值大于等于当前层级值 自增 1
        siblings.forEach((node, i) => i > index && node.zIndex >= this.zIndex && node.zIndex++);
      }
    }
    // 下移
    else if (zIndex === 'down') {
      // 寻找层级最大节点下标
      const index = siblings.findLastIndex((node) => this.zIndex >= node.zIndex);
      if (index !== -1) {
        // 当前层级值 = 层级最大节点层级值 - 1
        this.zIndex = siblings[index].zIndex - 1;
        // 之前的节点 层级值小于等于当前层级值 自减 1
        siblings.forEach((node, i) => i > index && node.zIndex <= this.zIndex && node.zIndex--);
      }
    }
    this.parent?.update?.();
    this.stage?.draw();
    this.stage?.handleRecord();
  }
  /**
   * 节点位置移动（网格坐标）
   */
  move(offset = { x: 0, y: 0 }, onlyMove = false) {
    const { parent, stage } = this;
    if (!parent) return;
    const gridCoord = this.getGridCoordByParentCoord({ x: this.x, y: this.y });
    (gridCoord.x += offset.x), (gridCoord.y += offset.y);
    const afterCoord = this.getParentCoordByGridCoord(gridCoord);
    (this.x = afterCoord.x), (this.y = afterCoord.y);
    this.updateParent();
    if (!stage || onlyMove) return;
    stage.transformer.update(this);
    // 重绘
    stage.draw();
    stage.handleRecord();
  }
  /**
   * 设置锚点集
   * @param {{id:any,name:string,x?:number,y?:number,radius?:number}[]|boolean} anchors
   */
  setAnchors(anchors, onlySet = false) {
    /** @type {any[]} 节点锚点集 */
    this.anchors = null;
    if (!anchors) return;
    // 开启锚点，生成默认四个边中心点的锚点
    else if (anchors === true) this.anchors = this.defaultAnchors;
    // 自定义锚点集
    else this.anchors = anchors;
    if (onlySet) return;
    // 重绘
    this.stage?.draw();
    this.stage?.handleRecord();
  }
  /**
   * 配置更新
   * @param {*} config
   */
  setConfig(config) {
    Object.assign(this, config);
    this.update();
    this.updateParent();
    this.stage?.transformer.update(this);
    // 重绘
    this.stage?.draw();
    this.stage?.handleRecord();
  }
  /**
   * 配置更新（渐变）
   * @param {*} params node config & tween config
   */
  to(params) {}
  /**
   * 节点转换为 Blob 对象
   */
  toBlob(callback, type = 'image/png', quality = 1) {
    const { width, height } = this;
    const canvas = new Canvas({ width, height });
    const ctx = canvas.getContext();
    this.drawMain(ctx, true);
    canvas.destroy(); // 销毁canvas
    return new Promise((resolve, reject) => {
      try {
        ctx.canvas.toBlob((blob) => (callback?.(blob), resolve(blob)), type, quality);
      } catch (error) {
        reject(error);
      }
    });
  }
  /**
   * 节点转换为 data URI
   */
  toDataURL(type = 'image/png', quality = 1) {
    const { width, height } = this;
    const canvas = new Canvas({ width, height });
    const ctx = canvas.getContext();
    this.drawMain(ctx, true);
    canvas.destroy(); // 销毁canvas
    return ctx.canvas.toDataURL(type, quality);
  }
}
